package com.siashan.toolkit.api.security.util.sign;


import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.HexUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.SignAlgorithm;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.crypto.digest.HmacAlgorithm;
import com.siashan.toolkit.api.security.CryptoConst;
import com.siashan.toolkit.api.security.properties.RestCryptoProperties;
import com.siashan.toolkit.api.security.properties.SignProperties;

import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Map;
import java.util.Random;
import java.util.Set;

/**
 * 接口签名工具类
 *
 * @author siashan
 * @since V1.0.1
 **/
public class SignUtil {

    private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    private static final Random RANDOM = new SecureRandom();


    /**
     * 获取随机字符串 Nonce Str
     *
     * @return String 随机字符串
     */
    public static String generateNonceStr() {
        char[] nonceChars = new char[32];
        for (int index = 0; index < nonceChars.length; ++index) {
            nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length()));
        }
        return new String(nonceChars);
    }


    /**
     * 获取当前时间戳，单位秒
     *
     * @return 当前时间戳
     */
    public static long getCurrentTimestamp() {
        return System.currentTimeMillis() / 1000;
    }

    /**
     * 获取当前时间戳，单位毫秒
     *
     * @return 当前时间戳
     */
    public static long getCurrentTimestampMs() {
        return System.currentTimeMillis();
    }


    /**
     * 生成签名. 注意，若含有sign_type字段，必须和signType参数保持一致。
     *
     * @param data           待签名数据
     * @param signProperties 签名配置
     * @param signType       签名方式
     * @return 签名
     * @exception Exception 异常
     */
    public static String generateSignature(final Map<String, Object> data, SignProperties signProperties, SignType signType) throws Exception {
        String key = signProperties.getKey();
        String rawSignture = getRawSignture(data, key, signType);
        String signture = null;
        switch (signType) {
            case MD5:
                signture = DigestUtil.md5Hex(rawSignture);
                break;
            case HMACSHA256:
                signture = DigestUtil.hmac(HmacAlgorithm.HmacSHA256, key.getBytes(CryptoConst.UTF8)).digestHex(rawSignture);
                break;
            case RSA2:
                byte[] signtureByte = SecureUtil.sign(SignAlgorithm.SHA256withRSA, signProperties.getPriKey(), signProperties.getPubKey()).sign(rawSignture.getBytes());
                signture = HexUtil.encodeHexStr(signtureByte);
                break;
            default:
                break;

        }
        // 先转大写，再做Base64转码
        signture = Base64.encode(signture.toUpperCase(), CryptoConst.UTF8);
        return signture;
    }

    /**
     * 封装加密明文
     *
     * @param data 请求报文
     * @param key  加密key
     * @return 加密明文
     */
    private static String getRawSignture(Map<String, Object> data, String key, SignType signType) {
        Set<String> keySet = data.keySet();
        String[] keyArray = keySet.toArray(new String[keySet.size()]);
        Arrays.sort(keyArray);
        StringBuilder sb = new StringBuilder();
        for (String k : keyArray) {
            if (k.equals(CryptoConst.FIELD_SIGN)) {
                continue;
            }
            Object val = data.get(k);
            if (null != val) {
                String valStr = String.valueOf(val).trim();
                if (valStr.length() > 0) {
                    // 参数值为空，则不参与签名
                    sb.append(k).append("=").append(valStr).append("&");
                }
            }

        }
        // 摘要算法key值不为空需参与字符串拼接，非对称算法无需拼接key值
        if (signType.isNeedKey()) {
            if (null != key && key.trim().length() > 0) {
                sb.append("key=").append(key);
            } else {
                sb.deleteCharAt(sb.length() - 1);
            }
        } else {
            sb.deleteCharAt(sb.length() - 1);
        }

        return sb.toString();
    }


    /**
     * 生成签名
     *
     * @param data                 Map类型数据
     * @param restCryptoProperties 配置信息
     * @return 签名是否正确
     * @throws Exception   异常
     */
    public static String generateSignature(Map<String, Object> data, RestCryptoProperties restCryptoProperties) throws Exception {
        Object signTypeObj = data.get(CryptoConst.FIELD_SIGN_TYPE);
        SignType signType = null == signTypeObj ? SignType.parseSignType(restCryptoProperties.getSign().getType()) : SignType.parseSignType(String.valueOf(signTypeObj));
        return generateSignature(data, restCryptoProperties.getSign(), signType);
    }


    /**
     * 判断签名是否正确，必须包含sign字段，否则返回false。
     *
     * @param data                 Map类型数据
     * @param restCryptoProperties 配置信息
     * @return 签名是否正确
     * @throws Exception  异常
     */
    public static boolean checkSignature(Map<String, Object> data, RestCryptoProperties restCryptoProperties) throws Exception {
        if (!data.containsKey(CryptoConst.FIELD_SIGN)) {
            return false;
        }
        String sign = String.valueOf(data.get(CryptoConst.FIELD_SIGN));
        String signature = generateSignature(data, restCryptoProperties);
        return signature.equals(sign);
    }


    public static void main(String[] args) {
        System.out.println(String.valueOf(null));
    }
}
